package com.winshare.saleAssistant.service.impl;

import com.aliyun.oss.ClientConfiguration;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.model.Bucket;
import com.aliyun.oss.model.CannedAccessControlList;
import com.aliyun.oss.model.CreateBucketRequest;
import com.aliyun.oss.model.StorageClass;
import com.winshare.saleAssistant.config.OssConfig;
import com.winshare.saleAssistant.service.NamingStrategy;
import com.winshare.saleAssistant.service.OssClient;
import com.winshare.saleAssistant.service.OssConsole;
import com.winshare.saleAssistant.service.OssTerminator;
import org.springframework.stereotype.Service;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class OssService implements OssConsole, OssClient, OssTerminator {

    private static final String HTTP_PROTOCOL = "https://";
    private static final String DOT = ".";
    private static final String SEPARATOR = "/";
    private String endpoint;
    private String accessKeyId;
    private String accessKeySecret;
    private String bucketName;
    private String folder;
    private NamingStrategy namingStrategy;
    OSSClient ossClient;
    private final OssConfig ossConfig;

    private OssService(OssConfig ossConfig) {
        this.ossConfig = ossConfig;
        this.namingStrategy = new DailyStrategy();
        this.endpoint = ossConfig.getEndpoint();
        this.accessKeyId = ossConfig.getAccessKeyId();
        this.accessKeySecret = ossConfig.getAccessKeySecret();
        this.bucketName = ossConfig.getBucketName();
        this.folder = ossConfig.getFolder();
        this.initOSSClient();
    }

    private void initOSSClient() {
        ClientConfiguration conf = this.initClientConfiguration();
        this.ossClient = new OSSClient(this.endpoint, this.accessKeyId, this.accessKeySecret, conf);
        if (!this.doesBucketExist(this.bucketName)) {
            throw new RuntimeException("bucket:" + this.bucketName + " not exist");
        }
    }

    private ClientConfiguration initClientConfiguration() {
        ClientConfiguration conf = new ClientConfiguration();
        conf.setMaxConnections(200);
        conf.setSocketTimeout(10000);
        conf.setMaxErrorRetry(5);
        return conf;
    }

    @Override
    public boolean doesBucketExist(String bucketName) {
        return this.ossClient.doesBucketExist(bucketName);
    }

    @Override
    public void createBucket(String bucketName) {
        this.createBucket(bucketName, 2);
    }

    @Override
    public void createBucket(String bucketName, int grade) {
        if (!this.doesBucketExist(bucketName)) {
            CreateBucketRequest createBucketRequest = new CreateBucketRequest(bucketName);
            CannedAccessControlList cannedAccessControlList;
            switch(grade) {
                case 1:
                    cannedAccessControlList = CannedAccessControlList.Private;
                    break;
                case 2:
                    cannedAccessControlList = CannedAccessControlList.PublicRead;
                    break;
                case 3:
                    cannedAccessControlList = CannedAccessControlList.PublicReadWrite;
                    break;
                default:
                    cannedAccessControlList = CannedAccessControlList.Private;
            }

            createBucketRequest.setCannedACL(cannedAccessControlList);
            createBucketRequest.setStorageClass(StorageClass.IA);
            this.ossClient.createBucket(createBucketRequest);
        }

    }

    @Override
    public List<String> listBuckets() {
        return (List)this.ossClient.listBuckets().stream().map(Bucket::getName).collect(Collectors.toList());
    }

    @Override
    public void deleteBucket(String bucketName) {
        if (this.doesBucketExist(bucketName)) {
            this.ossClient.deleteBucket(bucketName);
        }

    }

    @Override
    public boolean doesDirExist(String dir) {
        return this.ossClient.doesObjectExist(this.bucketName, dir);
    }

    @Override
    public void mkdir(String dir) {
        if (!this.doesDirExist(dir)) {
            this.ossClient.putObject(this.bucketName, dir, new ByteArrayInputStream(new byte[0]));
        }

    }

    @Override
    public void delDir(String dir) {
        if (this.doesDirExist(dir)) {
            this.ossClient.deleteObject(this.bucketName, dir);
        }
    }

    @Override
    public String upload(String key, String content) {
        String newKey = this.namingStrategy.getFolderName(this.folder) + this.namingStrategy.getFileName(key);
        this.ossClient.putObject(this.bucketName, newKey, new ByteArrayInputStream(content.getBytes()));
        return this.downloadUrl(newKey);
    }

    @Override
    public String upload(String key, byte[] content) {
        String newKey = this.namingStrategy.getFolderName(this.folder) + this.namingStrategy.getFileName(key);
        this.ossClient.putObject(this.bucketName, newKey, new ByteArrayInputStream(content));
        return this.downloadUrl(newKey);
    }

    @Override
    public String upload(String key, InputStream inputStream) {
        String newKey = this.namingStrategy.getFolderName(this.folder) + this.namingStrategy.getFileName(key);
        this.ossClient.putObject(this.bucketName, newKey, inputStream);
        return this.downloadUrl(newKey);
    }

    @Override
    public String upload(String key, File file) {
        String newKey = this.namingStrategy.getFolderName(this.folder) + this.namingStrategy.getFileName(key);
        this.ossClient.putObject(this.bucketName, newKey, file);
        return this.downloadUrl(newKey);
    }

    @Override
    public void delete(String key) {
        this.ossClient.deleteObject(this.bucketName, key);
    }

    @Override
    public void destroy() {
        this.ossClient.shutdown();
    }

    private String downloadUrl(String key) {
        if (this.endpoint.startsWith("https://")) {
            this.endpoint = this.endpoint.replaceFirst("https://", "");
        } else if (this.endpoint.startsWith("http://")) {
            this.endpoint = this.endpoint.replaceFirst("http://", "");
        }

        return "https://" + this.bucketName + "." + this.endpoint + "/" + key;
    }
}
